﻿namespace In66.Net.Repository.Dapper.Repositories
{
    public sealed class DapperRepository : IAdoExecuterWithQuerierRepository
    {
        internal IDbConnection? DbConnection { get; private set; }

        public void ChangeOrSetDbConnection(IDbConnection dbConnection)
        {
            if (dbConnection is null)
            {
                throw new ArgumentNullException(nameof(dbConnection));
            }

            DbConnection = dbConnection;
        }

        public void ChangeOrSetDbConnection(string connectionString, DbTypes dbType)
        {
            if (connectionString.IsNullOrWhiteSpace())
            {
                throw new ArgumentNullException(nameof(connectionString));
            }

            DbConnection = dbType switch
            {
                DbTypes.MYSQL => new MySqlConnection(connectionString),
                DbTypes.SQLSERVER => new MySqlConnection(connectionString),
                DbTypes.ORACLE => new MySqlConnection(connectionString),
                _ => throw new NotImplementedException()
            };
        }

        public bool HasDbConnection() => DbConnection is not null;

        public async Task<int> ExecuteAsync(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null)
         => await DbConnection.ExecuteAsync(sql, param, transaction, commandTimeout, commandType);

        public async Task<IDataReader> ExecuteReaderAsync(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        => await DbConnection.ExecuteReaderAsync(sql, param, transaction, commandTimeout, commandType);

        public async Task<object> ExecuteScalarAsync(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null)
         => await DbConnection.ExecuteScalarAsync(sql, param, transaction, commandTimeout, commandType);

        public async Task<T> ExecuteScalarAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null)
         => await DbConnection.ExecuteScalarAsync<T>(sql, param, transaction, commandTimeout, commandType);

        public async Task<IEnumerable<dynamic>?> QueryAsync(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(sql, param, transaction, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<T>?> QueryAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<object>?> QueryAsync(Type type, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(type, sql, param, transaction, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<TReturn>?> QueryAsync<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<TReturn>?> QueryAsync<TFirst, TSecond, TThird, TReturn>(string sql, Func<TFirst, TSecond, TThird, TReturn> map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<TReturn>?> QueryAsync<TFirst, TSecond, TThird, TFourth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<TReturn>?> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<TReturn>?> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<TReturn>?> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<IEnumerable<TReturn>?> QueryAsync<TReturn>(string sql, Type[] types, Func<object[], TReturn> map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryAsync(sql, types, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            return result.Any() ? result : null;
        }

        public async Task<T> QueryFirstAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryFirstAsync<T>(sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<dynamic> QueryFirstAsync(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryFirstAsync(sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<object> QueryFirstAsync(Type type, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryFirstAsync(type, sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryFirstOrDefaultAsync<T>(sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<dynamic> QueryFirstOrDefaultAsync(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryFirstOrDefaultAsync(sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<object> QueryFirstOrDefaultAsync(Type type, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QueryFirstOrDefaultAsync(type, sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<T> QuerySingleAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QuerySingleAsync<T>(sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<dynamic> QuerySingleAsync(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QuerySingleAsync(sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<object> QuerySingleAsync(Type type, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QuerySingleAsync(type, sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<T> QuerySingleOrDefaultAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QuerySingleOrDefaultAsync<T>(sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<dynamic> QuerySingleOrDefaultAsync(string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QuerySingleOrDefaultAsync(sql, param, transaction, commandTimeout, commandType);
            return result;
        }

        public async Task<object> QuerySingleOrDefaultAsync(Type type, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null, bool writeDb = false)
        {
            if (writeDb)
            {
                sql = string.Concat("/* ", RepositoryConsts.MAXSCALE_ROUTE_TO_MASTER, " */", sql);
            }

            var result = await DbConnection.QuerySingleOrDefaultAsync(type, sql, param, transaction, commandTimeout, commandType);
            return result;
        }
    }
}
